﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ModelDesignEduse.TableProjection;

namespace ModelDesignEduse.Framework
{
    public static class CRUD
    {
        static int current_language_id = 1;
        static int current_page_type_category_id = 1;
        static int current_page_type_id = 1;
        static int current_media_type_id = 1;
        static int current_media_object_id = 1;
        static int current_page_field_id = 1;
        static int current_element_id = 1;
        static int current_client_id = 1;

        public static Database DB
        {
            get
            {
                return Database.Instance;
            }
            set
            {
                Database.Instance = value;
            }
        }

        public static void InitCommons()
        {
            AddLanguage("Türkçe");
            AddLanguage("English");
            AddLanguage("Deutsch");
        }

        #region Language

        public static Language AddLanguage(string language_name)
        {
            Language lang = new Language();
            
            lang.language_name = language_name;

            if (DB.languages.Any(lng => lng.language_name == language_name))
                throw new EntityAlreadyExistsException("Language");

            lang.language_id = current_language_id++;
            
            DB.languages.Add(lang);

            return lang;
        }

        public static void RemoveLanguage(string language_name)
        {
            DB.languages.RemoveAll(lang => lang.language_name == language_name);
        }

        public static void UpdateLanguage(string oldLanguage, string newLanguage)
        {
            DB.languages[DB.languages.IndexOf((from lang in DB.languages
                                           where lang.language_name == oldLanguage
                                           select lang).First())].language_name = newLanguage;
        }

        public static Language GetLanguageById(int language_id)
        {
            return (from lang in DB.languages
                   where lang.language_id == language_id
                   select lang).First();
        }

        public static Language GetLanguageByName(string language_name)
        {
            return (from lang in DB.languages
                    where lang.language_name == language_name
                    select lang).First();
        }

        #endregion

        #region PageTypeCategory

        public static PageTypeCategory AddPageTypeCategory(string page_type_category_name)
        {
            PageTypeCategory pageTypCat = new PageTypeCategory();

            pageTypCat.page_type_category_name = page_type_category_name;

            if (DB.pageTypeCategories.Any(ptc => ptc.page_type_category_name == page_type_category_name))
                throw new EntityAlreadyExistsException("PageTypeCategory");

            pageTypCat.page_type_category_id = current_page_type_category_id++;

            DB.pageTypeCategories.Add(pageTypCat);

            return pageTypCat;
        }

        //Remove+Update

        public static PageTypeCategory GetPageTypeCategoryById(int page_type_category_id)
        {
            return (from ptc in DB.pageTypeCategories
                    where ptc.page_type_category_id == page_type_category_id
                    select ptc).First();
        }

        public static PageTypeCategory GetPageTypeCategoryByName(string page_type_category_name)
        {
            return (from ptc in DB.pageTypeCategories
                    where ptc.page_type_category_name == page_type_category_name
                    select ptc).First();
        }

        #endregion

        #region PageType

        public static PageType AddPageType(string page_type_name, string page_type_category_name, 
            string page_type_desc = "", int page_format_preview_image_id = 0)
        {
            PageType pageType = new PageType();

            pageType.page_type_name = page_type_name;

            if (DB.pageTypes.Any(ptc => ptc.page_type_name == page_type_name))
                throw new EntityAlreadyExistsException("PageType");

            pageType.page_type_id = current_page_type_id++;

            PageTypeCategory pageTypeCategory = CRUD.GetPageTypeCategoryByName(page_type_category_name);

            if (pageTypeCategory == null)
                throw new ReferencingEntityDoesNotExistException("PageType", "PageTypeCategory");

            pageType.page_type_category_id = pageTypeCategory.page_type_category_id;
            pageType.page_type_desc = page_type_desc;

            if (page_format_preview_image_id == 0)
                pageType.page_format_preview_image_id = 0;
            else if (DB.mediaObjects.Any(mobj => mobj.media_object_id == page_format_preview_image_id))
                pageType.page_format_preview_image_id = page_format_preview_image_id;
            else
                throw new ReferencingEntityDoesNotExistException("PageType", "MediaObject");

            DB.pageTypes.Add(pageType);

            return pageType;
        }

        //Remove+Update

        public static PageType GetPageTypeById(int page_type_id)
        {
            return DB.pageTypes.Find(pt => pt.page_type_id == page_type_id);
        }

        public static PageType GetPageTypeByName(string page_type_name)
        {
            return DB.pageTypes.Find(pt => pt.page_type_name == page_type_name);
        }

        public static List<PageType> GetPageTypesOfPageTypeCategory(string page_type_category_name)
        {
            PageTypeCategory ptc = CRUD.GetPageTypeCategoryByName(page_type_category_name);

            if (ptc != null)
                return DB.pageTypes.FindAll(pt => pt.page_type_category_id == ptc.page_type_category_id);
            return null;
        }

        #endregion

        #region MediaType

        public static void AddMediaType(string media_type_name)
        {
            MediaType mediaType = new MediaType();

            mediaType.media_type_name = media_type_name;

            if (DB.mediaTypes.Any(mt => mt.media_type_name == media_type_name))
                throw new EntityAlreadyExistsException("MediaType");

            mediaType.media_type_id = current_media_type_id++;

            DB.mediaTypes.Add(mediaType);
        }

        //Remove+Update

        public static MediaType GetMediaTypeById(int media_type_id)
        {
            return (from mt in DB.mediaTypes
                    where mt.media_type_id == media_type_id
                    select mt).First();
        }

        public static MediaType GetMediaTypeByName(string media_type_name)
        {
            return (from mt in DB.mediaTypes
                    where mt.media_type_name == media_type_name
                    select mt).First();
        }

        #endregion

        #region MediaObjectContent

        public static MediaObjectContent AddMediaObjectContent(int media_object_id, string language_name, string client_model,
            byte[] media_object_rendered_content, byte[] media_object_raw_content = null)
        {
            MediaObjectContent mediaObjectContent = new MediaObjectContent();

            Language language = GetLanguageByName(language_name);

            if (language == null)
                throw new ReferencingEntityDoesNotExistException("MediaObjectContent", "Language");

            Client client = CRUD.GetClientByModel(client_model);

            if (client == null)
                throw new ReferencingEntityDoesNotExistException("MediaObjectContent", "Client");

            if (DB.mediaObjectContents.Any(mobjcont => mobjcont.media_object_id == media_object_id &&
                mobjcont.language_id == language.language_id && mobjcont.client_id == client.client_id))
                throw new EntityAlreadyExistsException("MediaObjectContent");

            if (!DB.mediaObjects.Any(mobj => mobj.media_object_id == media_object_id))
                throw new ReferencingEntityDoesNotExistException("MediaObjectContent", "MediaObject");

            mediaObjectContent.media_object_id = media_object_id;
            mediaObjectContent.language_id = language.language_id;
            mediaObjectContent.client_id = client.client_id;

            if (media_object_rendered_content == null)
                throw new NotNullableEntityNullValueException("MediaObjectContent", "media_object_rendered_content");

            mediaObjectContent.media_object_rendered_content = media_object_rendered_content;
            mediaObjectContent.media_object_raw_content = media_object_raw_content;

            return mediaObjectContent;
        }

        public static MediaObjectContent GetMediaObjectContentByAll(int media_object_id, string language_name, string client_model)
        {
            Language language = CRUD.GetLanguageByName(language_name);

            if (language == null)
                return null;

            Client client = CRUD.GetClientByModel(client_model);

            if (client == null)
                return null;

            return DB.mediaObjectContents.Find(moc => moc.language_id == language.language_id && moc.client_id == client.client_id);
        }

        public static List<MediaObjectContent> GetMediaObjectContents(Predicate<MediaObjectContent> predicate)
        {
            return DB.mediaObjectContents.FindAll(predicate);
        }

        #endregion

        #region PageMediaObject

        public static PageMediaObject AddPageMediaObject(int page_id, int media_object_id)
        {
            PageMediaObject pageMediaObject = new PageMediaObject();

            if (DB.pageMediaObjects.Any(pmo => pmo.page_id == page_id && pmo.media_object_id == media_object_id))
                throw new EntityAlreadyExistsException("PageMediaObject");

            if (!DB.pages.Any(page => page.page_id == page_id))
                throw new ReferencingEntityDoesNotExistException("PageMediaObject", "Page");

            if (!DB.mediaObjects.Any(mo => mo.media_object_id == media_object_id))
                throw new ReferencingEntityDoesNotExistException("PageMediaObject", "MediaObject");

            pageMediaObject.page_id = page_id;
            pageMediaObject.media_object_id = media_object_id;
            DB.pageMediaObjects.Add(pageMediaObject);

            return pageMediaObject;
        }

        public static List<MediaObject> GetMediaObjectsOfPage(int page_id)
        {
            List<PageMediaObject> pageMedObj = DB.pageMediaObjects.FindAll(pmo => pmo.page_id == page_id);

            return (from pmo in pageMedObj
                   join mo in DB.mediaObjects
                   on pmo.media_object_id equals mo.media_object_id
                   select mo).ToList();
        }

        #endregion

        #region MediaObject

        public static MediaObject AddMediaObject(string media_object_name, string media_type_name, int page_field_id = 0)
        {
            MediaObject mediaObject = new MediaObject();

            if (DB.mediaObjects.Any(medobj => medobj.media_object_name == media_object_name))
                throw new EntityAlreadyExistsException("MediaObject");

            MediaType mediaType = CRUD.GetMediaTypeByName(media_type_name);

            if (mediaType == null)
                throw new ReferencingEntityDoesNotExistException("MediaObject", "MediaType");

            mediaObject.media_object_name = media_object_name;
            mediaObject.media_type_id = mediaType.media_type_id;

            if (page_field_id == 0)
                mediaObject.page_field_id = 0;
            else if (DB.pageFields.Any(pf => pf.page_field_id == page_field_id))
                mediaObject.page_field_id = page_field_id;
            else
                throw new ReferencingEntityDoesNotExistException("MediaObject", "PageField");

            mediaObject.media_object_id = current_media_object_id++;

            DB.mediaObjects.Add(mediaObject);

            return mediaObject;
        }

        public static MediaObject GetMediaObjectById(int media_object_id)
        {
            return DB.mediaObjects.Find(mobj => mobj.media_object_id == media_object_id);
        }

        public static MediaObject GetMediaObjectByName(string media_object_name)
        {
            return DB.mediaObjects.Find(mobj => mobj.media_object_name == media_object_name);
        }

        public static MediaObject GetMediaObjectByPageFieldId(int page_field_id)
        {
            return DB.mediaObjects.Find(mobj => mobj.page_field_id == page_field_id);
        }

        #endregion

        #region PageField

        public static PageField AddPageField(string page_field_title, string media_type_name, string page_type_name)
        {
            PageField pageField = new PageField();

            PageType pageType = CRUD.GetPageTypeByName(page_type_name);

            if (pageType == null)
                throw new ReferencingEntityDoesNotExistException("PageField", "PageType");

            MediaType mediaType = CRUD.GetMediaTypeByName(media_type_name);

            if (mediaType == null)
                throw new ReferencingEntityDoesNotExistException("PageField", "MediaType");

            if (DB.pageFields.Any(pf => pf.page_field_title == page_field_title && pf.page_type_id == pageType.page_type_id))
                throw new EntityAlreadyExistsException("PageField");

            pageField.page_field_title = page_field_title;
            pageField.page_type_id = pageType.page_type_id;
            pageField.media_type_id = mediaType.media_type_id;
            pageField.page_field_id = current_page_field_id++;
            DB.pageFields.Add(pageField);

            return pageField;
        }

        public static PageField GetPageFieldByID(int page_field_id)
        {
            return DB.pageFields.Find(pf => pf.page_field_id == page_field_id);
        }
        
        public static PageField GetPageFieldBySigniture(string page_field_title, int page_type_id)
        {
            return DB.pageFields.Find(pf => pf.page_field_title == page_field_title && pf.page_type_id == page_type_id);
        }

        public static List<PageField> GetPageFieldOfPageType(string page_type_name)
        {
            PageType pageType = CRUD.GetPageTypeByName(page_type_name);

            return DB.pageFields.FindAll(pf => pf.page_type_id == pageType.page_type_id);
        }

        #endregion

        #region ElementType

        public static ElementType AddElementType(string element_type_name)
        {
            ElementType elementType = new ElementType();

            elementType.element_type_name = element_type_name;

            DB.elementTypes.Add(elementType);

            return elementType;
        }

        public static ElementType GetElementTypeById(int element_type_id)
        {
            return DB.elementTypes.Find(et => et.element_type_id == element_type_id);
        }

        public static ElementType GetElementTypeByName(string element_type_name)
        {
            return DB.elementTypes.Find(et => et.element_type_name == element_type_name);
        }

        #endregion

        #region Element

        public static Element AddElement(int element_type_id)
        {
            Element element = new Element();

            if (!DB.elementTypes.Any(et => et.element_type_id == element_type_id))
                throw new ReferencingEntityDoesNotExistException("Element", "ElementType");

            element.element_id = current_element_id++;

            DB.elements.Add(element);

            return element;
        }

        public static Element GetElementById(int element_id)
        {
            return DB.elements.Find(et => et.element_id == element_id);
        }

        #endregion

        #region ElementName

        //controls
        public static ElementName AddElementName(int element_id, int language_id, string element_name)
        {
            ElementName elementName = new ElementName();

            elementName.element_id = element_id;
            elementName.language_id = language_id;
            elementName.element_name = element_name;

            DB.elementNames.Add(elementName);

            return elementName;
        }

        public static ElementName GetElementNameByLanguage(int element_id, int language_id)
        {
            return DB.elementNames.Find(en => en.element_id == element_id && en.language_id == language_id);
        }


        #endregion

        #region Page

        public static Page AddPage(int page_type_id)
        {
            /*Element element = new Element();

            element.element_id = current_element_id++;
            element.element_type_id = GetElementTypeByName("Page");*/

            Element baseElement = AddElement(GetElementTypeByName("Page").element_type_id);
            Page page = new Page();

            page.page_id = baseElement.element_id;

            if (!DB.pageTypes.Any(pt => pt.page_type_id == page_type_id))
                throw new ReferencingEntityDoesNotExistException("Page", "PageType");

            page.page_type_id = page_type_id;

            DB.pages.Add(page);

            return page;
        }

        public static Page GetPageById(int page_id)
        {
            return DB.pages.Find(pg => pg.page_id == page_id);
        }

        #endregion

        #region Client

        public static Client AddClient(string client_name, string vendor, string model)
        {
            Client client = new Client();

            client.client_name = client_name;
            client.client_vendor = vendor;
            client.client_model = model;

            client.client_id = current_client_id++;

            DB.clients.Add(client);

            return client;
        }

        public static Client GetClientById(int client_id)
        {
            return DB.clients.Find(cl => cl.client_id == client_id);
        }

        public static Client GetClientByModel(string model)
        {
            return DB.clients.Find(cl => cl.client_model == model);
        }

        #endregion
    }
}
